/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package model;

import java.util.LinkedList;
import java.util.Random;

/**
 *
 * @author Stupi
 */
public class KMeans implements Clustering{
    
    
    private int width;
    private int height;
    private Point [] centroids;
    private LinkedList<Point> [] clusters;
    private Point [] points;
    private boolean [] end;
    
    
    @Override
    public void cluster() {
        setRandomCentroids();
        
        
        
        while (!isEnd()) {
            clearClusters();
            fillClusters();
            updateCentroids();
           
        }
    }

    public KMeans(int width, int height, int numOfClusters) {
        
        this.width = width;
        this.height = height;
        centroids = new Point[numOfClusters];
        clusters = new LinkedList [numOfClusters];
        points = InitRandomPoints.getPoints();
        end = new boolean [numOfClusters];
        for (int i = 0; i < end.length; i++) {
            end[i] = false;
        }
    }
    
    public void setRandomCentroids () {
        
        Random random = new Random();
        int indent = 20;
        
        int count = 0;
        while (count < centroids.length) {
            int x = random.nextInt(width - 5);
            int y = random.nextInt(height - 5);
            int seq = -(count+1);
            
            Point p = new Point();
            p.setX(x);
            p.setY(y);
            p.setSequenceNumber(seq);
            
            boolean has = false;
            for (int i = 0; i < count; i++) {
                if (centroids[i].equals(p)) 
                    has = true;
            }
            
            if (!has) {
                centroids[count] = p;
                count++;
            }
        }  
    }
    
    public void fillClusters () {
        
        double [] clusterDesicion = new double[clusters.length];
        
        for (int i = 0; i < points.length; i++) {
            
            Point p = points[i];
            
            for (int j = 0; j < clusters.length; j++) {
                
                Point c = centroids[j];
                clusterDesicion[j] = DistanceCalculation.calculateDistance(p, c);
            }
            
            int index = minimumDistanceIndex(clusterDesicion);
            clusters[index].add(p);
        }
        
        
    }
    
    protected int minimumDistanceIndex (double [] clusterDesicion) {
        
        int min = 0;
        for (int i = 0; i < clusterDesicion.length; i++) {
            if (clusterDesicion[min] > clusterDesicion[i])
                min = i;
        }
        return min;
    }
    
    public void updateCentroids () {
        
        for (int i = 0; i < clusters.length; i++) { 
            
            double sumX = 0;
            double sumY = 0;
            int sizeOfCluster = clusters[i].size();
            
            
            
            for (Point point : clusters[i]) {
                sumX = sumX + point.getX();
                sumY = sumY + point.getY();
            }
            
            int newX = (int) Math.round(sumX/sizeOfCluster);
            int newY = (int) Math.round(sumY/sizeOfCluster);
            
            Point p = centroids[i];
            
            int oldX = p.getX();
            int oldY = p.getY();
            
            if (oldX == newX && oldY == newY) {
                end[i] = true;
            }else {
                p.setX(newX);
                p.setY(newY);
            }
        }
        
        
    }
    
    
    protected boolean isEnd () {
        for (int i = 0; i < end.length; i++) {
            if (end [i] == false)
                return false;
        }
        return true;
    }
    
    
    public LinkedList<Point>[] getClusters() {
        return clusters;
    }
    
    public void clearClusters() {
        for (int i = 0; i < clusters.length; i++) {
            clusters[i] = new LinkedList<Point>();
        }
        
    }
    
    
}
